#pragma once
#include <iostream>
#include <fstream>
#include <jsoncpp/json/json.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <memory>
#include "bundle.h"

#include <experimental/filesystem>

using namespace std;

namespace Util
{
    namespace fs = std::experimental::filesystem;
    class FileUtil
    {
    private:
        string _filename;

    public:
        FileUtil(const string &filename) : _filename(filename) {}
        bool Remove()
        {
            
            remove(_filename.c_str());
            return true;
        }
        size_t FileSize()
        {
            struct stat st;
            int n = stat(_filename.c_str(), &st);
            if (n == -1)
            {
                cout << "get file stat failed" << endl;
                return -1;
            }
            return st.st_size;
        }
        time_t LastAtime()
        {
            struct stat st;
            int n = stat(_filename.c_str(), &st);
            if (n == -1)
            {
                cout << "get file stat failed" << endl;
                return -1;
            }
            return st.st_atime;
        }
        time_t LastMTime()
        {
            struct stat st;
            int n = stat(_filename.c_str(), &st);
            if (n == -1)
            {
                cout << "get file stat failed" << endl;
                return -1;
            }
            return st.st_mtime;
        }
        string FileName()
        {
            size_t pos = _filename.find_last_of("/");
            if (pos == string::npos)
            {
                return _filename;
            }
            return _filename.substr(pos + 1);
        }
        bool GetPosLen(string *body, size_t pos, size_t len)
        {
            size_t fsize = this->FileSize();
            if (pos + len > fsize)
            {
                cout << "get file len is error" << endl;
                return false;
            }
            ifstream ifs;
            ifs.open(_filename, ios::binary);
            if (ifs.is_open() == false)
            {
                cout << "read open file failed" << endl;
                return false;
            }
            ifs.seekg(pos, ios::beg);
            body->resize(len);
            ifs.read(&(*body)[0], len);
            if (ifs.good() == false)
            {
                cout << "get file content failed" << endl;
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }
        bool GetContent(string *content)
        {
            size_t fsize = FileSize();
            return GetPosLen(content, 0, fsize);
        }
        bool SetContent(const string &body)
        {
            ofstream ofs;
            ofs.open(_filename, ios::binary);
            if (ofs.is_open() == false)
            {
                cout << "write open file failed" << endl;
                return false;
            }
            ofs.write(body.c_str(), body.size());
            if (ofs.good() == false)
            {
                cout << "write content file failed" << endl;
                ofs.close();
                return false;
            }
            ofs.close();
            return true;
        }
        bool Compress(const string &packname)
        {
            string body;
            if (GetContent(&body) == false)
            {
                cout << "Compress file content failed" << endl;
                return false;
            }
            string pack = bundle::pack(bundle::LZIP, body);
            FileUtil packfile(packname);
            if (packfile.SetContent(pack) == false)
            {
                cout << "Compress write file content failed" << endl;
                return false;
            }
            return true;
        }
        bool UnCompress(const string &filename)
        {
            string body;
            if (GetContent(&body) == false)
            {
                cout << "UnCompress file content failed" << endl;
                return false;
            }
            string unpack = bundle::unpack(body);
            FileUtil packfile(filename);
            if (packfile.SetContent(unpack) == false)
            {
                cout << "UnCompress write file content failed" << endl;
                return false;
            }
            return true;
        }
        bool Exists()
        {
            return fs::exists(_filename);
        }
        bool CreateDirctory()
        {
            if (Exists())
                return true;
            return fs::create_directories(_filename);
        }
        bool ScanDirectory(vector<string> *arr)
        {
            for (auto &p : fs::directory_iterator(_filename))
            {
                if (fs::is_directory(p) == true)
                {
                    continue;
                }
                arr->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }
    };
    class JsonUtil
    {
    public:
        static bool Serialize(const Json::Value &root, string *str)
        {
            Json::StreamWriterBuilder swb;
            unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            sw->write(root, &ss);
            *str = ss.str();
            return true;
        }
        static bool UnSerialize(const string &str, Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            unique_ptr<Json::CharReader> cr(crb.newCharReader());
            string err;
            bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);
            if (ret == false)
            {
                cout << "parse error" << endl;
                return false;
            }
            return true;
        }
    };
}